Automation Test Practice

Follow me on GitHub

Page Object Design in Python and Selenium

I have seen many people using python for web application automation test, however many of them forget the general best practice. In this article, I will give a complete example on Page Object designing in web application automation test. If you are not familiar with Page Object, feel free to refer my previous post Page Object Model and Fluent Design I and Page Object Model and Fluent Design II.

In my example, I am using pytest as unit test framework and PyCharm as IDE. If you are not familiar with pytest fixture, refer Data Driven Test in pytest –fixture. In addition, I strongly suggest to set a seperate virtual environment for your project — PyCharm can help you on this easily.

Firstly let’s build a BasePage as the parent of all page classes. To simplify, I have not put too many functions in the class so far.

class BasePage(object):
    def __init__(self, driver, timeout = 30):
        self.driver = driver
        
    def find_element(self, *locator):
        return self.driver.find_element(*locator)

    def get_title(self):
        return self.driver.title

    def get_url(self):
        return self.driver.current_url

Then we build a real page. you can see clearly it extends from BasePage.

from page.basepage import BasePage
from .homepagelocator import HomePageLocator
from .workspacepage import WorkspacePage


class HomePage(BasePage):
    def __init__(self, driver):
        self.locator = HomePageLocator
        super().__init__(driver)

        self.user_name_field = self.find_element(*self.locator.USER_NAME)
        self.password_field = self.find_element(*self.locator.PASSWORD)
        self.login_button = self.find_element(*self.locator.LOGIN_BUTTON)

    def login(self, user_name, password):
        self.user_name_field.clear()
        self.user_name_field.send_keys(user_name)
        self.password_field.clear()
        self.password_field.send_keys(password)
        self.login_button.click()

        return WorkspacePage(self.driver)

In terms of the elements in the page, Selenium Python does not support PageFactory as Selenium Java API does. We can put the locators of the elements in a separate file to make the maintenance easier. You can also put them in the same page class actually.

from selenium.webdriver.common.by import By

class HomePageLocator(object):
    USER_NAME = (By.ID, 'username')
    PASSWORD = (By.ID, 'password')
    LOGIN_BUTTON = (By.ID, 'loginButton')

The next is page test. Same as I introduced in JAVA section, we need a general page test class to set driver, open page, and etc. Here we need to use the pytest fixture feature. Firstly in the conftest.py file we build following fixture. BTW: “request” is a pytest built-in fixture. with request.cls.driver = web_driver, any class use this fixture will get an attribute driver automatically.

import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

@pytest.fixture(scope="class")
def driver_init(request):
    web_driver = webdriver.Chrome(ChromeDriverManager().install())
    web_driver.maximize_window()
    request.cls.driver = web_driver

    yield

    web_driver.close()

Here is the general page test class. All other page test class should extends from this calss. And it use fixture “driver_init

import pytest

@pytest.mark.usefixtures("driver_init")
class BaseTest:
    def open_page(self, base_url):
        self.driver.get(base_url)

Finally here comes a real page test.

import pytest

from page.myproduct.homepage import HomePage
from test.basetest import BaseTest

from properties import *


class Test_Regression(BaseTest):

    def test_login(self):
        self.open_page(BASE_URL)
        homepage = HomePage(self.driver)
        workspagepage = homepage.login(USER_NAME, PASSWORD)
        assert workspagepage.get_title() == 'My Workspace'

if __name__ == "__main__":
  pytest.main(["-q","pagetest.py"])

In both BaseTest and Test_Regression, there is no attribute definition of driver, however since we use the driver_init fixture, we can use it directly in the page test class. Actually this is a powerful feature of python — Independent Injection. Following is the overview on the project file structure.
python_test.png

If you can integrate the data driven approach I introduced in another post, a python + selenium automation test framework is coming to a rough stage. Wish you have fun~~~

Back To Homepage